home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / Newton Sample Code 1.1 / Connection Kit / Built In MetaDatas-1 / Card File MetaData next >
Encoding:
Text File  |  1994-03-09  |  18.3 KB  |  670 lines  |  [TEXT/MPS ]

  1. /*
  2.     Copyright:    © 1993-1994 by Apple Computer, Inc., all rights reserved.
  3. */
  4.  
  5. // ----------------------------------------------------------------------------------------
  6. //        I M P O R T
  7. // ----------------------------------------------------------------------------------------
  8. NamesImport := func( textFrame, store, importNotes )
  9.     begin
  10.     local soupFrame, error;
  11.  
  12.     soupFrame := {};
  13.  
  14.     soupFrame.cardType := 0;
  15.  
  16.     if (textFrame.cardType) then
  17.         begin
  18.         local temp := FindStringInArray(editTypes.cardType.choices, textFrame.cardType);
  19.         if temp then
  20.             soupFrame.cardType := temp;
  21.         end;
  22.  
  23.     if (textFrame.honorific) then
  24.         begin
  25.         if (soupFrame.name = nil) then
  26.             soupFrame.name := {class:'person};
  27.         soupFrame.name.honorific := textFrame.honorific;
  28.         end;
  29.  
  30.     if (textFrame.firstName) then
  31.         begin
  32.         if (soupFrame.name = nil) then
  33.             soupFrame.name := {class:'person};
  34.         soupFrame.name.first := textFrame.firstName;
  35.         end;
  36.  
  37.     if (textFrame.lastname) then
  38.         begin
  39.         if (soupFrame.name = nil) then
  40.             soupFrame.name := {class:'person};
  41.         soupFrame.name.last := SetClass (textFrame.lastname, 'name);
  42.         end;
  43.  
  44.     if (textFrame.title) then
  45.         begin
  46.         if (soupFrame.name = nil) then
  47.             soupFrame.name := {class:'person};
  48.         soupFrame.name.title := textFrame.title;
  49.         end;
  50.  
  51.     if (textFrame.company) then
  52.         soupFrame.company := SetClass (textFrame.company,'company);
  53.  
  54.     if (textFrame.address) then
  55.         begin
  56.         soupFrame.address := textFrame.address;
  57.         SetClass(soupFrame.address,'address);
  58.         end;
  59.  
  60.     if (textFrame.address2) then
  61.         begin
  62.         soupFrame.address2 := textFrame.address2;
  63.         SetClass(soupFrame.address2,'address);
  64.         end;
  65.  
  66.     if (textFrame.city) then
  67.         soupFrame.city := textFrame.city;
  68.  
  69.     if (textFrame.region) then
  70.         soupFrame.region := textFrame.region;
  71.  
  72.     if (textFrame.postalcode) then
  73.         soupFrame.postal_code := textFrame.postalcode;
  74.  
  75.     if (textFrame.country) then
  76.         soupFrame.country := textFrame.country;
  77.  
  78.     if (textFrame.email) then
  79.         soupFrame.email := textFrame.email;
  80.  
  81.     local tPhones, tPhoneTypes,    i, temp, foundType;
  82.  
  83.     tPhones := '[phone1,phone2,phone3,phone4];
  84.     tPhoneTypes := '[phone1type,phone2type,phone3type,phone4type];
  85.     foundType := nil;
  86.  
  87.     soupFrame.phones := [];
  88.     for i := 0 to 3 do
  89.         begin
  90.         if (textFrame.(tPhones[i])) then
  91.             begin
  92.             temp := textFrame.(tPhones[i]);
  93.             if temp then
  94.                 begin
  95.                 local moniker := textFrame.(tPhoneTypes[i]);
  96.  
  97.                 local class := 'phone;            // default to phone
  98.                 if moniker then
  99.                     begin
  100.                     local index := FindStringInArray (editTypes.phoneType.choices, moniker);
  101.                     if index <> nil then
  102.                         class := phoneClasses[index];
  103.                     end;
  104.                 AddArraySlot(soupFrame.phones, SetClass( temp, class) );
  105.                 end;
  106.             end;
  107.         end;
  108.  
  109.     if (textFrame.birthday) then
  110.         soupFrame.bday := StringToDate(textFrame.birthday);
  111.  
  112.     if (textFrame.labels and (StrLen(textFrame.labels) > 0)) then
  113.         soupFrame.labels := MakeSymbol (textFrame.labels);
  114.  
  115.     if textFrame.sorton Exists then
  116.         begin
  117.         if StrEqual(textFrame.sorton, :getSortonString ('company)) then
  118.             begin
  119.             if soupFrame.company then
  120.                 soupFrame.sorton := soupFrame.company;
  121.             else
  122.                 soupFrame.sorton := SetClass( Clone(""), 'company );
  123.             end;
  124.         else
  125.             if StrEqual(textFrame.sorton, :getSortonString ('name)) then
  126.                 begin
  127.                 if (soupFrame.name <> nil) and (soupFrame.name.last <> nil) then
  128.                     soupFrame.sorton := soupFrame.name.last;
  129.                 end;
  130.         end;
  131.     if soupFrame.sorton = nil then
  132.         soupFrame.sorton := SetClass( Clone(""), 'name );
  133.  
  134.     if (importNotes <> nil) then
  135.         begin
  136.         if (textFrame.notes <> nil) and (store <> nil) then
  137.             soupFrame.notes := StringToParagraphArray( store, textFrame.notes, kWSforNamesNote);
  138.         end;
  139.     else if textFrame.originalFrame and textFrame.originalFrame.notes then
  140.         soupFrame.notes := textFrame.originalFrame.notes;
  141.  
  142.     soupFrame;
  143.     end;
  144.  
  145. //-------------------------------------------------------------------------------------------------
  146. //                        M E T A  D A T A
  147. //-------------------------------------------------------------------------------------------------
  148. CardFileMeta :=  {
  149.     soup: "Names",
  150.     name: "Name File",
  151.     version: 2,
  152.     soupInfo: {applications: ['cardFile], itemNames: ["name", "names"] },
  153.     soupIndicies: [{structure: 'slot, path: 'sorton, type: 'String}],
  154.  
  155.     getLocalizedLabel: func (slot)
  156.         begin
  157.         local localeBundle := GetLocale();
  158.         return localeBundle.(slot);
  159.         end,
  160.  
  161.     attributeFunctions: {
  162.         cityLabel: func ()
  163.             return :GetLocalizedLabel('cityLabel),
  164.         regionLabel: func ()
  165.             return :GetLocalizedLabel('regionLabel),
  166.         postalCodeLabel: func ()
  167.             return :GetLocalizedLabel('postalCodeLabel),
  168.         citySize: func ()
  169.             begin            // account for locale
  170.             local len := strlen(:GetLocalizedLabel('regionLabel));
  171.             if len = 5 then return 27;
  172.             else if len = 6 then return 25;
  173.             else return 23;
  174.             end,
  175.         postalCodeSize: func ()
  176.             begin
  177.             local len := strlen(:GetLocalizedLabel('postalCodeLabel));
  178.             if len = 8 then return 20;
  179.             else if len = 9 then return 19;
  180.             else return 18;
  181.             end,
  182.         },
  183.  
  184.     // make sure there's a sorton slot since it's indexed
  185.     newEntry: {sortOn: SetClass(Clone(""), 'name), phones: []},
  186.  
  187.     defaultDefinitions: {
  188.         honorific:    {label:"Ms./Mr.",        size:5, path: [pathExpr: 'name, 'honorific]},
  189.         firstname:    {label:"First Name",            path: [pathExpr: 'name, 'first]},
  190.         lastname:    {label:"Last Name",                path: [pathExpr: 'name, 'last]},
  191.         title:        {label:"Title",                    path: [pathExpr: 'name, 'title]},
  192.         company:    {label:"Company"},
  193.         address:    {label:"Address Line 1"},
  194.         address2:    {label:"Address Line 2"},
  195.         city:         {label:'cityLabel},
  196.         region:     {label:'regionLabel},
  197.         postalCode:    {label:'postalCodeLabel,            path: 'postal_code,    justify:'right},
  198.         country:    {label:"Country"},
  199.         email:        {label:"E-Mail"},
  200.         phone1:     {label:"Phone 1",        compareScript: 'phone,         type:'phone,         justify:'right},
  201.         phone1type:    {label:"Key 1",            compareScript: 'phonetype,    type:'phoneType},
  202.         phone2:     {label:"Phone 2",        compareScript: 'phone,        type:'phone,         justify:'right},
  203.         phone2type:    {label:"Key 2",            compareScript: 'phonetype,    type:'phoneType},
  204.         phone3:     {label:"Phone 3",        compareScript: 'phone,        type:'phone,         justify:'right},
  205.         phone3type:    {label:"Key 3",            compareScript: 'phonetype,    type:'phoneType},
  206.         phone4:     {label:"Phone 4",        compareScript: 'phone,        type:'phone,         justify:'right},
  207.         phone4type:    {label:"Key 4",            compareScript: 'phonetype,    type:'phoneType},
  208.         birthday:    {label:"Birthday",        type:'date,     path: 'bday},
  209.         labels:        {label:"Folder",        type:'folder,    path: 'labels},
  210.         sorton:        {type:'sortonType,        compareScript: 'sorton},
  211.         cardType:    {type:'cardType,        compareScript: 'cardType},
  212.         notes:        {label: "Notes",        type: 'text},
  213.         },
  214.  
  215.     displayInfo: [
  216.         {slot:'honorific,                    hidden:TRUE},
  217.         'lastname,
  218.         'firstname,
  219.         {slot:'title,                        hidden:TRUE},
  220.         {slot:'company,        size:20},
  221.         {slot:'address,        size:20,        hidden:TRUE},
  222.         {slot:'address2,    size:20,        hidden:TRUE},
  223.         {slot:'city,        size:20,        hidden:TRUE},
  224.         {slot:'region,        size:6,            hidden:TRUE},
  225.         {slot:'postalCode,    size:10,        hidden:TRUE},
  226.         {slot:'country,        size:6,            hidden:TRUE},
  227.         {slot:'email,        size:20,        hidden:TRUE},
  228.         'phone1,
  229.         {slot:'phone1type,    size:7},
  230.         'phone2,
  231.         {slot:'phone2type,    size:7},
  232.         {slot:'phone3,                        hidden:TRUE},
  233.         {slot:'phone3type,    size:7,            hidden:TRUE},
  234.         {slot:'phone4,                        hidden:TRUE},
  235.         {slot:'phone4type,    size:7,            hidden:TRUE},
  236.         {slot:'birthday,    size:12,        hidden:TRUE},
  237.         {slot:'labels,                        hidden:TRUE},
  238.         {slot:'sorton,                        hidden:TRUE},
  239.         {slot:'cardType,                    hidden:TRUE},
  240.         {slot: 'notes,                        hidden: TRUE},
  241.         ],
  242.  
  243.     editInfo: [
  244.          {slot:'company,    size:27},
  245.          {slot:'sorton,                    sameline:TRUE},
  246.          'honorific,
  247.          {slot:'firstname,    size:15,    sameline:TRUE,    label: "Name"},
  248.          {slot:'lastname,     size:22,    sameline:TRUE,    label: ""},
  249.          {slot:'title,        size:50},
  250.          {slot:'address,    size:50,                    label: "Address"},
  251.          {slot:'address2,    size:50,                    label: ""},
  252.          {slot:'city,        size: 'citySize},
  253.          {slot:'region,     size:17,    sameline:TRUE},
  254.          {slot:'postalCode, size:'postalCodeSize},
  255.          {slot:'country,    size:21,    sameline:TRUE},
  256.          {slot:'phone1,        size:27},
  257.          {slot:'phone1Type,             sameline:TRUE,    label: ""},
  258.          {slot:'phone2,        size:27},
  259.          {slot:'phone2Type,             sameline:TRUE,    label: ""},
  260.          {slot:'phone3,        size:27},
  261.          {slot:'phone3Type,             sameline:TRUE,    label: ""},
  262.          {slot:'phone4,        size:27},
  263.          {slot:'phone4Type,             sameline:TRUE,    label: ""},
  264.          {slot:'email,        size:27},
  265.          {slot:'birthday,    size:14,    sameline:TRUE},
  266.          'labels,
  267.          {slot:'cardType,                sameline:TRUE,    label: "    Card Style"},
  268.          {slot: 'notes,        uneditable: true, size: 50},
  269.         ],
  270.     column0width: 7,
  271.  
  272.     exportInfo: [
  273.         'honorific,
  274.         'firstname,
  275.         'lastname,
  276.         'title,
  277.         'company,
  278.         'address,
  279.         'address2,
  280.         'city,
  281.         'region,
  282.         'postalCode,
  283.         'country,
  284.         'email,
  285.         'phone1,
  286.         'phone1type,
  287.         'phone2,
  288.         'phone2type,
  289.         'phone3,
  290.         'phone3type,
  291.         'phone4,
  292.         'phone4type,
  293.         'birthday,
  294.         'labels,
  295.         'sorton,
  296.         'cardType,
  297.         'notes
  298.         ],
  299.  
  300.     editTypes: {
  301.             phoneType: {
  302.                 type:'PICKLIST,
  303.                 choices:[
  304.                     "Phone",
  305.                     "Home",
  306.                     "Work",
  307.                     "Fax",
  308.                     "Car",
  309.                     "Beeper",
  310.                     "Mobile",
  311.                     "Other"
  312.                     ]
  313.                 },
  314.             cardType:    {
  315.                 type:'PICKLIST,
  316.                 label:"Card Style",
  317.                 choices:[
  318.                     "Style 1",
  319.                     "Style 2",
  320.                     "Style 3",
  321.                     "Style 4"
  322.                     ]
  323.                 },
  324.             sortonType:    {
  325.                 type:'PICKLIST,
  326.                 label:"List By",
  327.                 choices:[
  328.                     "Last name",
  329.                     "Company",
  330.                     ]
  331.                 },
  332.         },
  333.  
  334.     phoneClasses:[    'phone,
  335.                     'homePhone,
  336.                     'workPhone,
  337.                     'faxPhone,
  338.                     'carPhone,
  339.                     'beeperPhone,
  340.                     'mobilePhone,
  341.                     'otherPhone
  342.                     ],
  343.  
  344.     compareScripts: {
  345.         phone:
  346.             func(entry, args)
  347.                 begin
  348.                 local result := nil;
  349.                 if entry.phones then
  350.                     begin
  351.                     local it := 3;
  352.                     if args.slotname = 'phone1 then
  353.                         it := 0;
  354.                     else if args.slotname = 'phone2 then
  355.                         it := 1;
  356.                     else if args.slotname = 'phone3 then
  357.                         it := 2;
  358.                     if it < Length(entry.phones) then
  359.                         begin
  360.                         result := entry.phones[it];
  361.                         end;
  362.                     end;
  363.                 return result;
  364.                 end,
  365.         phoneType:
  366.             func(entry, args)
  367.                 begin
  368.                 local result := nil;
  369.                 if entry.phones then
  370.                     begin
  371.                     local it := 3;
  372.                     if args.slotname = 'phone1type then
  373.                         it := 0;
  374.                     else if args.slotname = 'phone2type then
  375.                         it := 1;
  376.                     else if args.slotname = 'phone3type then
  377.                         it := 2;
  378.                     if it < Length(entry.phones) then
  379.                         begin
  380.                         result := :mapPhoneClassForExport(ClassOf(entry.phones[it]));
  381.                         end;
  382.                     end;
  383.                 return result;
  384.                 end,
  385.         sorton:
  386.             func(entry, args)
  387.                 begin
  388.                 local result := nil;
  389.                 if entry.sorton then
  390.                     result := :getSortonString(ClassOf(entry.sorton));
  391.                 return result;
  392.                 end,
  393.         cardType:
  394.             func(entry, args)
  395.                 return :exportCardType(entry.cardType);
  396.             },
  397.  
  398.     // pass in 'name or 'company and this will return the proper string
  399.     getSortonString: func (theSymbol)
  400.         begin
  401.         if theSymbol = 'company then
  402.             return editTypes.sortonType.choices [1];
  403.         else
  404.             return editTypes.sortonType.choices [0];
  405.         end,
  406.  
  407.     commonImport: NamesImport,
  408.     tabImport:    func (textFrame, store)
  409.         :entryValidation (:commonImport (textFrame, store, true), store),
  410.     editImport:    func (textFrame, store)
  411.         :entryValidation (:commonImport (textFrame, store, nil), store),
  412.     // This one's just here for wizard import
  413.     import: func (textFrame)
  414.         :commonImport (textFrame, nil, nil),
  415.  
  416.     // ----------------------------------------------------------------------------------------
  417.     //        E X P O R T  U T I L I T I E S
  418.     // ----------------------------------------------------------------------------------------
  419.     mapPhoneClassForExport: func (phoneClass)
  420.         begin
  421.             local exportClass := "Phone";
  422.             local index := 0;
  423.             loop
  424.                 begin
  425.                 if index >= Length(phoneClasses) then break;
  426.                 else
  427.                     begin
  428.                     if phoneClass = phoneClasses[index] then
  429.                         begin
  430.                         exportClass := editTypes.phoneType.choices[index];
  431.                         break;
  432.                         end;
  433.                     end;
  434.                 index := index + 1;
  435.                 end;
  436.             return exportClass;
  437.         end,
  438.  
  439.     exportCardType: func (cardType)
  440.         begin
  441.             local result := nil;
  442.             if cardType then
  443.                 begin
  444.                 if cardType < Length(editTypes.cardType.choices) then
  445.                     result := editTypes.cardType.choices[cardType];
  446.                 end;
  447.             return result;
  448.         end,
  449.  
  450.     exportText: func (data)
  451.         begin
  452.         local result := nil;
  453.         if data then
  454.             begin
  455.             // First make an array of the text slot indexes in the order of the top value in the viewbounds
  456.             // Need to do this because the paragraphs are stored in the array in the order the user enters them,
  457.             // not in the top to bottom order I want.
  458.             local tops := [];
  459.             foreach index, datum in data do
  460.                 if (datum.text Exists) and not datum.copyProtection then
  461.                     AddArraySlot (tops, {top: datum.viewbounds.top, index: index});
  462.             Sort(tops, '|<|, 'top);
  463.  
  464.             foreach which in tops do
  465.                 begin
  466.                 theText := data[which.index].text;
  467.                 if result then
  468.                     result := Stringer([result, "\n", theText]);
  469.                 else
  470.                     result := theText;
  471.                 end;
  472.             end;
  473.         if not result then
  474.             result := "";
  475.         return result;
  476.         end,
  477.  
  478.     // ----------------------------------------------------------------------------------------
  479.     //        E X P O R T
  480.     // ----------------------------------------------------------------------------------------
  481.     commonExport: func( soupFrame, store )
  482.         begin
  483.         local textFrame, i, numPhones;
  484.  
  485.         textFrame := {originalFrame: soupFrame};
  486.         if (soupFrame.sorton) then
  487.             textFrame.sorton := :getSortonString(ClassOf(soupFrame.sorton));
  488.  
  489.         local temp := :exportCardType (soupFrame.cardType);
  490.         if temp then
  491.             textFrame.cardType := temp;
  492.  
  493.         if (soupFrame.labels) then
  494.             if store = nil then
  495.                 textFrame.labels := SPrintObject (soupFrame.labels);
  496.             else
  497.                 textFrame.labels := GetFolderName(store, soupFrame.labels);
  498.  
  499.         if (soupFrame.name) then
  500.             begin
  501.             if (soupFrame.name.first) then
  502.                 textFrame.firstname := soupFrame.name.first;
  503.             if (soupFrame.name.last) then
  504.                 textFrame.lastname := soupFrame.name.last;
  505.             if (soupFrame.name.honorific) then
  506.                 textFrame.honorific := soupFrame.name.honorific;
  507.             if (soupFrame.name.title) then
  508.                 textFrame.title := soupFrame.name.title;
  509.             end;
  510.  
  511.         if (soupFrame.company) then
  512.             textFrame.company := soupFrame.company;
  513.  
  514.         if (soupFrame.address) then
  515.             textFrame.address := soupFrame.address;
  516.  
  517.         if (soupFrame.address2) then
  518.             textFrame.address2 := soupFrame.address2;
  519.  
  520.         if (soupFrame.city) then
  521.             textFrame.city := soupFrame.city;
  522.  
  523.         if (soupFrame.region) then
  524.             textFrame.region := soupFrame.region;
  525.  
  526.         if (soupFrame.postal_code) then
  527.             textFrame.postalcode := soupFrame.postal_code;
  528.  
  529.         if (soupFrame.country) then
  530.             textFrame.country := soupFrame.country;
  531.  
  532.         if (soupFrame.email) then
  533.             textFrame.email := soupFrame.email;
  534.  
  535.         if (soupFrame.phones) then
  536.             begin
  537.             numPhones := Length(soupFrame.phones);
  538.             if (numPhones>0) then
  539.                 begin
  540.                 textFrame.phone1 := soupFrame.phones[0];
  541.                 textFrame.phone1type := :mapPhoneClassForExport(ClassOf(soupFrame.phones[0]));
  542.                 end;
  543.             if (numPhones>1) then
  544.                 begin
  545.                 textFrame.phone2 := soupFrame.phones[1];
  546.                 textFrame.phone2type := :mapPhoneClassForExport(ClassOf(soupFrame.phones[1]));
  547.                 end;
  548.             if (numPhones>2) then
  549.                 begin
  550.                 textFrame.phone3 := soupFrame.phones[2];
  551.                 textFrame.phone3type := :mapPhoneClassForExport(ClassOf(soupFrame.phones[2]));
  552.                 end;
  553.             if (numPhones>3) then
  554.                 begin
  555.                 textFrame.phone4 := soupFrame.phones[3];
  556.                 textFrame.phone4type := :mapPhoneClassForExport(ClassOf(soupFrame.phones[3]));
  557.                 end;
  558.             end;
  559.  
  560.         if not (soupFrame.bday = nil) then
  561.             textFrame.birthday := ShortDateStr(soupFrame.bday, 0);
  562.  
  563.         if (soupFrame.notes Exists) then
  564.             begin
  565.             textFrame.notes := :exportText (soupFrame.notes);
  566.             end;
  567.  
  568.         textFrame;
  569.         end,
  570.  
  571.         tabExport: func( soupFrame, store )
  572.             :commonExport ( soupFrame, store ),
  573.  
  574.         editExport: func( soupFrame, store )
  575.             :commonExport ( soupFrame, store ),
  576.  
  577.         browserExport: func( soupFrame, store )
  578.             :commonExport ( soupFrame, store ),
  579.  
  580.     // ----------------------------------------------------------------------------------------
  581.     //        V A L I D A T I O N
  582.     // ----------------------------------------------------------------------------------------
  583.     namesValidation: {
  584.         company:        {validType: 'string, nilOK: 'delete},
  585.         address:        {validType: 'string, nilOK: 'delete},
  586.         address2:        {validType: 'string, nilOK: 'delete},
  587.         city:             {validType: 'string, nilOK: 'delete},
  588.         region:         {validType: 'string, nilOK: 'delete},
  589.         postal_code:    {validType: 'string, nilOK: 'delete},
  590.         country:        {validType: 'string, nilOK: 'delete},
  591.         email:            {validType: 'string, nilOK: 'delete},
  592.         phones:            {validType: 'array, nilOK: 'delete},
  593.         bday:                {validType: 'int, nilOK: 'delete},
  594.         labels:            {validType: 'labels, nilOK: 'delete},
  595.         sorton:            {validType: 'string, nilOK: 'delete},
  596.         cardType:        {validType: 'int, nilOK: 'delete},
  597.         notes:            {validType: 'note, noteMaxWidth: 237, nilOK: 'delete},
  598.     },
  599.  
  600.     nameValidation: {
  601.         honorific:    {validType: 'string, nilOK: 'delete},
  602.         first:    {validType: 'string, nilOK: 'delete},
  603.         last:    {validType: 'string, nilOK: 'delete},
  604.         title:        {validType: 'string, nilOK: 'delete},
  605.         },
  606.  
  607.     namesParaFormat: {
  608.         lineHeight:                18,
  609.         maxRight:                    237,
  610.         defaultFontSpec:    bor(tsFancy,tsSize(18)),
  611.         maxBottom:                149,
  612.         },
  613.  
  614.     entryValidation: func (toValidate, store)
  615.         begin
  616.         toValidate := ValidateSlots ("Names", toValidate, namesValidation);
  617.         if IsFrame (toValidate) then
  618.             begin
  619.             if toValidate.name Exists then
  620.                 begin
  621.                 local result := ValidateSlots ("Names", toValidate.name, nameValidation);
  622.                 if IsFrame (toValidate.name) then
  623.                     toValidate.name := result;
  624.                 else
  625.                     toValidate := result;
  626.                 end;
  627.             if IsFrame (toValidate) then
  628.                 begin
  629.                 // Do the special validation here
  630.  
  631.                 // Make sure the class of sortOn is either company or name
  632.                 if toValidate.sorton Exists then
  633.                     if (ClassOf(toValidate.sorton) <> 'name) and (ClassOf(toValidate.sorton) <> 'company) then
  634.                         SetClass (toValidate.sorton, 'name);
  635.  
  636.                 // Make sure the phone strings are really 'string
  637.                 if toValidate.phones Exists then
  638.                     begin
  639.                     foreach slot in toValidate.phones do
  640.                         begin
  641.                         if (not IsInstance(slot, 'string)) or
  642.                              (Length(slot) MOD 2 <> 0) or
  643.                              (ExtractWord (slot, Length(slot) - 2) <> 0) then
  644.                             return "a Names entry contained an invalid phone string";
  645.                         local found := nil;
  646.                         local slotClass := ClassOf(slot);
  647.                         foreach choice in phoneClasses do
  648.                             if choice = slotClass then
  649.                                 begin
  650.                                 found := true;
  651.                                 break;
  652.                                 end;
  653.                         if not found then
  654.                             return "a Names entry contained an invalid phone type";
  655.                         end;
  656.                     end;
  657.  
  658.                     // Make sure the name frame has a class of 'person
  659.                     if toValidate.name Exists then
  660.                         SetClass (toValidate.name, 'person);
  661.  
  662.                 if toValidate.notes <> nil then
  663.                     toValidate := ValidateNotes(toValidate, 'notes, namesParaFormat, store);
  664.                 end;
  665.             end;
  666.         return toValidate;
  667.         end,
  668.     };
  669.  
  670.